# functools 高阶函数和可调用对象上的操作
import functools


# sorted(iterable, key=functools.cmp_to_key(cmp_func)) 支持从2到3的转换


@functools.total_ordering
class Student:
    def __init__(self, lastname, firstname):
        self.lastname = lastname


        self.firstname = firstname

    def __eq__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) ==
                (other.lastname.lower(), other.firstname.lower()))

    def __lt__(self, other):
        return ((self.lastname.lower(), self.firstname.lower()) <
                (other.lastname.lower(), other.firstname.lower()))


def showarg(*args, **kw):
    print('args', args)
    print('kw', kw)


p1 = functools.partial(showarg, 1, 2, 3)
p1()  # args (1, 2, 3) kw {}
p1(4, 5, 6)  # args (1, 2, 3, 4, 5, 6) kw {}
p1(a='python', b='itcast')  # args (1, 2, 3) kw {'a': 'python', 'b': 'itcast'}


def add(a, b):
    return a + b


plus1 = functools.partial(add, 1)
print(plus1(2))  # 3

sorted_reverse = functools.partial(sorted, reverse=True)
print(sorted_reverse([1, 3, 2, 3, 6, 4]))  # [6, 4, 3, 3, 2, 1]


class Cell:
    def __init__(self):
        self._alive = False

    @property
    def alive(self):
        return self._alive

    def set_state(self, state):
        self._alive = bool(state)

    set_alive = functools.partialmethod(set_state, True)
    set_dead = functools.partialmethod(set_state, False)


c = Cell()
print(c.alive)  # False
c.set_alive()
print(c.alive)  # True

print(functools.reduce(lambda x, y: x * y, [1, 2, 3, 4, 5]))  # 120


@functools.singledispatch
def fun(arg, verbose=False):
    if verbose:
        print('Let me just say,', end='')
    print(arg)


@fun.register(int)
def _(arg, verbose=False):
    if verbose:
        print("Strength in numbers, eh?", end=" ")
    print(arg)


@fun.register(list)
def _(arg, verbose=False):
    if verbose:
        print('Enumerate this,')
    for i, elem in enumerate(arg):
        print(i, elem)


fun('Hello,world.')  # Hello,world.
fun('test.', verbose=True)  # Let me just say,test.
fun(42, verbose=True)  # Strength in numbers, eh? 42
fun(['egg', 'tomato'], verbose=True)  # Enumerate this,0 egg 1 tomato


# wraps函数：添加装饰器后由于函数名和函数的doc发生了改变，所有此方法来消除这样的副作用

def note(func):
    def wrapper(func):
        print('note something')
        return func()

    return wrapper


@note
def test():
    print('I am test')


# test()
# print(test.__doc__)
# print(test.__name__)

'''
结果如下：
note something
I am test
wrapper function # 这里结果就发生了改变，本来应该是test function,是因为装饰器影响的
wrapper          # 这里的name属性也被装饰器影响到了，本应该是 test
'''


def note(func):
    @functools.wraps(func)
    def wrapper(func):
        print('note something')
        return func()

    return wrapper


@note
def test():
    print('I am test')


# test()
# print(test.__doc__)
# print(test.__name__)
'''
结果如下
note something
I am test
test function # 使用@functools.wraps(func)装饰器后，这个就不会受到影响了
test
'''
